﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.Linq;
using System.Data.Common;
using AppEnvironment.Lib;
using System.Transactions;
using AppEnvironment.Lib.Interfaces;

namespace AppEnvironment.Lib
{

    public abstract class DataScope : IStateFull, IDisposable
    {
        protected DataContext ContextBase { get; set; }
        private TransactionScope CurrentTransaction { get; set; }
        private int CommitsNeeded { get; set; }
        private int InstanceCount { get; set; }
        private bool RollBackRequested { get; set; }
        
        public DataScope()
        {
            ContextBase = GetNewContext();
            ContextBase.CommandTimeout = 20;
        }

        protected abstract DataContext GetNewContext();

        public void BeginTransaction()
        {
            BeginTransaction(TimeSpan.Zero);
        }

        public void BeginTransaction(TimeSpan timeOut)
        {
            if (CommitsNeeded == 0)
            {
                if (CurrentTransaction == null)
                {
                    if (timeOut == TimeSpan.Zero)
                    {
                        timeOut = new TimeSpan(0, 1, 0); //default timeout
                    }
                    CurrentTransaction = new TransactionScope(TransactionScopeOption.Required, timeOut);
                }
            }
            CommitsNeeded++;
        }


        public void RollBack()
        {
            //Rollback was requested at least once, so everything must roll back
            CommitsNeeded = 0;
            RollBackRequested = true;
        }

        public void Commit()
        {
            if (CommitsNeeded > 0)
            {
                CommitsNeeded--;
                if (CommitsNeeded == 0)
                {
                    if (CurrentTransaction != null)
                    {
                        try
                        {
                            if (!RollBackRequested)
                            {
                                CurrentTransaction.Complete();
                            }
                        }
                        finally
                        {
                            CurrentTransaction.Dispose();
                            CurrentTransaction = null;
                        }
                    }
                }
            }
        }

        public List<T> Paginate<T>(IQueryable<T> query, int pageNumber, int pageSize, out bool hasMorePages)
        {
            int startRow = (pageNumber - 1) * pageSize;
            var result = (from x in query select x).Skip(startRow).Take(pageSize + 1);
            List<T> list = result.ToList();
            hasMorePages = list.Count > pageSize;
            if (hasMorePages)
            {
                list.RemoveAt(list.Count - 1);
            }
            return list;
        }

        public List<T> Paginate<T>(IQueryable<T> query, int pageNumber, int pageSize, out int totalPages)
        {
            totalPages = 0;
            int startRow = (pageNumber - 1) * pageSize;
            var newQuery =
                from x in query
                select new
                {
                    Total = query.Count(),
                    Result = x
                };

            var result = newQuery.Skip(startRow).Take(pageSize);
            var list = result.ToList();

            int totalCount = list == null ? 0 : (list.Count == 0 ? 0 : list[0].Total);
            totalPages = Convert.ToInt32(Math.Ceiling(Convert.ToDouble(totalCount) / Convert.ToDouble(pageSize)));

            List<T> listResult = (from x in list select x.Result).ToList();

            return listResult;
        }

        #region IDisposable Members

        public void Dispose()
        {
            if (InstanceCount > 0)
            {
                InstanceCount--;

                if (InstanceCount == 0)
                {
                    if (CurrentTransaction != null)
                    {
                        if ((CommitsNeeded == 0) && (!RollBackRequested))
                        {
                            CurrentTransaction.Complete();
                        }
                        CurrentTransaction.Dispose();
                        CurrentTransaction = null;
                    }

                    if (ContextBase != null)
                    {
                        ContextBase.Dispose();
                        ContextBase = null;
                    }
                    CommitsNeeded = 0;
                    InstanceCount = 0;
                    RollBackRequested = false;
                    if (OnDisposed != null)
                    {
                        OnDisposed(this, EventArgs.Empty);
                    }
                }
            }
        }
        
        #endregion


        #region IStateFull Members

        public void NewInstanceRequested()
        {
            InstanceCount++;
        }

        public event EventHandler OnDisposed;

        #endregion

    }
}
